package com.th.campusmatchbackend.service.impl;




import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.xiaoymin.knife4j.core.util.CollectionUtils;
import com.th.campusmatchbackend.common.ErrorCode;
import com.th.campusmatchbackend.enums.TeamStatusEnum;
import com.th.campusmatchbackend.exception.BusinessException;
import com.th.campusmatchbackend.mapper.TeamMapper;
import com.th.campusmatchbackend.model.domain.Team;
import com.th.campusmatchbackend.model.domain.User;
import com.th.campusmatchbackend.model.domain.UserTeam;
import com.th.campusmatchbackend.model.dto.TeamQuery;
import com.th.campusmatchbackend.model.request.TeamJoinRequest;
import com.th.campusmatchbackend.model.request.TeamQuitRequest;
import com.th.campusmatchbackend.model.request.TeamUpdateRequest;
import com.th.campusmatchbackend.model.vo.TeamUserVO;
import com.th.campusmatchbackend.model.vo.UserVO;
import com.th.campusmatchbackend.service.TeamService;
import com.th.campusmatchbackend.service.UserService;
import com.th.campusmatchbackend.service.UserTeamService;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
* @author taoha
* @description 针对表【team(队伍)】的数据库操作Service实现
* @createDate 2023-12-27 16:34:01
*/
@Service
public class TeamServiceImpl extends ServiceImpl<TeamMapper, Team>
    implements TeamService {


    @Resource
    UserTeamService userTeamService;
    @Resource
    UserService userService;

    @Resource
    private RedissonClient redissonClient;


    @Transactional(rollbackFor = Exception.class)
    @Override
    public long addTeam(Team team, User loginUser) {
        //1. 请求参数是否为空
        if(team==null){
            throw new BusinessException(ErrorCode.NULL_ERROR);
        }
        //2. 是否登录，未登录不允许创建
        if(loginUser==null){
            throw new BusinessException(ErrorCode.NULL_LOGIN);
        }
        final  long userId = loginUser.getId();
        //3. 校验信息
            // a. 队伍人数 >1 且 <=20
        Integer maxNum = Optional.ofNullable(team.getMaxNum()).orElse(0);
        if(maxNum<1||maxNum>20){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍人数不符合标准");
        }
            // b.队伍标题<=20
        String name  = team.getName();
        if(StringUtils.isBlank(name)||name.length()>20){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍标题不满足标准");
        }
            // c.描述<=512
        String description = team.getDescription();
        if(StringUtils.isBlank(description)||description.length()>512){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍描述过长");
        }
        // d.status是否公开(int) 不传默认为0（公开）
        int status = Optional.ofNullable(team.getStatus()).orElse(0);
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        //todo
        if (statusEnum == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "队伍状态不满足要求");
        }


        // e.如果status是加密状态，一定要有密码，解密码<=32
            // f .超时时间 > 当前时间
        String password = team.getPassword();
        if(TeamStatusEnum.SECRET.equals(statusEnum)){
            if(StringUtils.isBlank(password)||password.length()>32){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码设置不正确");
            }
        }
        // g.校验用户最多创建5个队伍
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId",userId);
        //用this是因为用的是本Service的方法
        long hasTeamNum = this.count(queryWrapper);
        if(hasTeamNum>=5){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户最多创建5个的队伍");
        }

        //4.插入队伍信息到队伍表
        team.setId(null);
        team.setUserId(userId);
        boolean result = this.save(team);
        Long teamId = team.getId();

        if(!result||teamId==null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"创建队伍失败");
        }
        //5.插入用户=>队伍关系到 关系表
        UserTeam userTeam  = new UserTeam();
        userTeam.setUserId(userId);
        userTeam.setTeamId(teamId);
        userTeam.setJoinTime(new Date());
         result = userTeamService.save(userTeam);
         if(!result){
             throw new BusinessException(ErrorCode.PARAMS_ERROR,"创建队伍失败");
         }


        return teamId;
    }

    @Override
    public List<TeamUserVO> listTeams(TeamQuery teamQuery,boolean isAdmin) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        //组合查询条件
        //1. 从请求参数中取出队伍名称等查询条件，如果存在则作为查询条件
        //     * teamId
        //     * id 列表
        //     * 搜索关键词（同时对队伍名称和描述搜索）
        //     * 队伍名称
        //     * 描述
        //     * 最大人数
        //     * 用户id
        //     * 0 - 公开，1 - 私有，2 - 加密
       if(teamQuery!=null){
            Long id = teamQuery.getId();
            if(id!=null&&id>0){
                queryWrapper.eq("id",id);
            }
        }

        List<Long> idList = teamQuery.getIdList();
        if(CollectionUtils.isNotEmpty(idList)){
            queryWrapper.in("id",idList);
        }
        //3. 可以通过某个关键词同时对名称和描述查询
        String searchText = teamQuery.getSearchText();
        if(StringUtils.isNotBlank(searchText)){
            queryWrapper.and(qw -> qw.like("name",searchText).or().like("description",searchText));
        }


        String name = teamQuery.getName();
        if(StringUtils.isNotBlank(name)){
            queryWrapper.like("name",name);
        }
        String description = teamQuery.getDescription();
        if(StringUtils.isNotBlank(description)){
            queryWrapper.like("description",description);
        }
        Integer maxNum = teamQuery.getMaxNum();
        if(maxNum!=null&&maxNum>0){
            queryWrapper.eq("maxNum",maxNum);
        }
        Long userId = teamQuery.getUserId();
        if(userId!=null&&userId>0){
            queryWrapper.eq("userId",userId);
        }

        Integer status = teamQuery.getStatus();
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(status);
        if(statusEnum==null){
            statusEnum  = TeamStatusEnum.PUBLIC;
        }
        //4. 只有管理员才能查看加密还有非公开的房间
        //todo 有bug
        if (!isAdmin && statusEnum.equals(TeamStatusEnum.PRIVATE)) {
            throw new BusinessException(ErrorCode.NULL_AUTH);
        }


        queryWrapper.eq("status",statusEnum.getValue());

        //2. 不展示已过期的队伍（根据过期时间筛选）
        //expireTime is null or expireTime > now()

        queryWrapper.and(qw -> qw.gt("expireTime",new Date()).or().isNull("expireTime"));

        List<Team> teamList = this.list(queryWrapper);
        if(CollectionUtils.isEmpty(teamList)){
            return new ArrayList<>();
        }
       List<TeamUserVO> teamUserVOList = new ArrayList<>();
        //关联查询创建人的用户信息
        for (Team team : teamList) {
            userId = team.getUserId();
            if (userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);

            if(user!=null){
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user,userVO);
                teamUserVO.setCreateUser(userVO);
            }

            teamUserVOList.add(teamUserVO);


        }
        //todo
        //5. 关联查询已加入队伍的用户信息
        //6. 关联查询已加入队伍的用户信息（可能会很耗费性能，建议大家用自己写 SQL 的方式实现
        return teamUserVOList;
    }

    @Override
    public List<TeamUserVO> listMyJoinTeams(TeamQuery teamQuery) {
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        if(teamQuery!=null){
            Long id = teamQuery.getId();
            if(id!=null&&id>0){
                queryWrapper.eq("id",id);
            }
        }
        //3. 可以通过某个关键词同时对名称和描述查询
        String searchText = teamQuery.getSearchText();
        if(StringUtils.isNotBlank(searchText)){
            queryWrapper.and(qw -> qw.like("name",searchText).or().like("description",searchText));
        }
        List<Long> idList = teamQuery.getIdList();
        if(CollectionUtils.isNotEmpty(idList)){
            queryWrapper.in("id",idList);
        }

        //2. 不展示已过期的队伍（根据过期时间筛选）
        //expireTime is null or expireTime > now()

        queryWrapper.and(qw -> qw.gt("expireTime",new Date()).or().isNull("expireTime"));

        Long userId = teamQuery.getUserId();
        List<Team> teamList = this.list(queryWrapper);
        if(CollectionUtils.isEmpty(teamList)){
            return new ArrayList<>();
        }
        List<TeamUserVO> teamUserVOList = new ArrayList<>();

        //关联查询创建人的用户信息
        for (Team team : teamList) {
            userId = team.getUserId();
            if (userId == null) {
                continue;
            }
            User user = userService.getById(userId);
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);

            if(user!=null){
                UserVO userVO = new UserVO();
                BeanUtils.copyProperties(user,userVO);
                teamUserVO.setCreateUser(userVO);
            }

            teamUserVOList.add(teamUserVO);


        }
        //todo
        //5. 关联查询已加入队伍的用户信息
        //6. 关联查询已加入队伍的用户信息（可能会很耗费性能，建议大家用自己写 SQL 的方式实现
        return teamUserVOList;
    }

    @Override
    public boolean updateTeam(TeamUpdateRequest teamUpdateRequest, User loginUser) {
        if(teamUpdateRequest==null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long id = teamUpdateRequest.getId();
        if(id==null||id<=0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team oldTeam = this.getById(id);
        if(oldTeam==null){
            throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在");
        }
        //只有管理员或者队伍的创建者才可以修改
        if(!oldTeam.getUserId().equals(loginUser.getId()) &&!userService.isAdmin(loginUser)){
            throw new BusinessException(ErrorCode.NULL_AUTH);
        }
        //如果队伍状态改为加密，必须要有密码
        TeamStatusEnum statusEnum = TeamStatusEnum.getEnumByValue(teamUpdateRequest.getStatus());
        if(statusEnum.equals(TeamStatusEnum.SECRET)){
            if(StringUtils.isBlank(teamUpdateRequest.getPassword())){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"加密房间必须要设置密码");
            }
        }
        Team updateTeam = new Team();
        BeanUtils.copyProperties(teamUpdateRequest,updateTeam);
        return this.updateById(updateTeam);

    }

    @Override
    public boolean joinTeam(TeamJoinRequest teamJoinRequest, User loginUser) {
        if(teamJoinRequest == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Long teamId = teamJoinRequest.getTeamId();
        if(teamId==null||teamId<=0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if(team==null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍不存在");
        }
        Date expireTime = team.getExpireTime();
        if(expireTime!=null&&expireTime.before(new Date())){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍已过期");
        }

        Integer status = team.getStatus();
        TeamStatusEnum teamStatusEnum = TeamStatusEnum.getEnumByValue(status);
        if(teamStatusEnum.PRIVATE.equals(teamStatusEnum)){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"禁止加入私密队伍");
        }

        String password = teamJoinRequest.getPassword();
        if(teamStatusEnum.SECRET.equals(teamStatusEnum)){
            if(StringUtils.isBlank(password)||!password.equals(team.getPassword())){
                throw new BusinessException(ErrorCode.PARAMS_ERROR,"密码错误");
            }
        }

        //数据库的查询要放到下面，减少查询时间
        //该用户已加入的队伍的数量
        long userId = loginUser.getId();
        //只有一个线程能获取到锁
        RLock lock = redissonClient.getLock("campusmatch:join_team");
        try{
            //抢到锁并执行
            while(true){
                if(lock.tryLock(0,-1, TimeUnit.MICROSECONDS)){
                    System.out.println("getLock: "+Thread.currentThread().getId());
                    QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("userId",userId);
                    long hasJoinNum = userTeamService.count(userTeamQueryWrapper);
                    if(hasJoinNum>5){
                        throw new BusinessException(ErrorCode.PARAMS_ERROR,"最多创建或加入5个队伍");
                    }

                    //不能重复加入已加入的队伍
                    userTeamQueryWrapper = new QueryWrapper<>();
                    userTeamQueryWrapper.eq("userId",userId);
                    userTeamQueryWrapper.eq("teamId",teamId);
                    long hasUserJoinTeam = userTeamService.count(userTeamQueryWrapper);
                    if(hasUserJoinTeam>0){
                        throw new BusinessException(ErrorCode.PARAMS_ERROR,"用户已加入该队伍");
                    }

                    //已加入队伍的人数

                    long teamHasJoinNum = this.countTeamUserByTeamId(teamId);
                    if(teamHasJoinNum>=team.getMaxNum()){
                        throw new BusinessException(ErrorCode.PARAMS_ERROR,"队伍已满");
                    }
                    //修改队伍信息
                    UserTeam userTeam = new UserTeam();
                    userTeam.setUserId(userId);
                    userTeam.setTeamId(teamId);
                    userTeam.setJoinTime(new Date());

                    return userTeamService.save(userTeam);
                }
            }
        }catch (InterruptedException e){
            log.error("doCacheRecommendUser error", e);
            return false;
        }finally {
            //只能释放自己的锁
            if(lock.isHeldByCurrentThread()){
                System.out.println("unLock: " + Thread.currentThread().getId());
                lock.unlock();
            }
        }

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean quitTeam(TeamQuitRequest teamQuitRequest, User loginUser) {
       if(teamQuitRequest == null){
           throw new BusinessException(ErrorCode.PARAMS_ERROR);
       }
        Long teamId = teamQuitRequest.getTeamId();
       if(teamId == null|| teamId<=0){
           throw new BusinessException(ErrorCode.PARAMS_ERROR);
       }

        Team team = this.getById(teamId);
       if(team == null){
           throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在");
       }
        long userId = loginUser.getId();
       UserTeam queryUserTeam = new UserTeam();
       queryUserTeam.setUserId(userId);
       queryUserTeam.setTeamId(teamId);
       QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>(queryUserTeam);
        long count = userTeamService.count(queryWrapper);
        if(count == 0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"为加入队伍");
        }
        long teamHasJoinNum = this.countTeamUserByTeamId(teamId);
        //队伍只剩下一人,解散
        if(teamHasJoinNum==1){
            //因为两种情况最后都是要移除关系的，所以抽出逻辑放最后

            //删除队伍
            this.removeById(teamId);
        }else{
            //队伍至少还剩下两人
            //是队长
            if(team.getUserId()==userId){
                //把队伍转移给最早加入的用户
                //1.查询已加入队伍的所有用户和加入时间
                QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
                userTeamQueryWrapper.eq("teamId",teamId);
                //在sql末尾添加语句
                userTeamQueryWrapper.last("order by id asc limit 2");
                List<UserTeam> userTeamList = userTeamService.list(userTeamQueryWrapper);
                if(CollectionUtils.isEmpty(userTeamList)||userTeamList.size()<=1){
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR);
                }
                UserTeam nextUserTeam = userTeamList.get(1);
                Long nextUserTeamUserId = nextUserTeam.getUserId();
                //更新当前队伍的队长
                Team updateTeam = new Team();
                updateTeam.setId(teamId);
                updateTeam.setUserId(nextUserTeamUserId);
                boolean result = this.updateById(updateTeam);
                if(!result){
                    throw new BusinessException(ErrorCode.PARAMS_ERROR,"更新队伍失败");
                }

            }
        }

        return userTeamService.remove(queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteTeam(long id, User loginUser) {
        //1. 校验请求参数(是否存在)
        Team team = getTeamById(id);
        Long teamId = team.getId();
        //校验你是不是队伍de队长
        if(!team.getUserId().equals(loginUser.getId())){
            throw new BusinessException(ErrorCode.NULL_AUTH,"无访问权限");
        }
        //移除所有加入队伍的关联信息
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId",teamId);
        boolean result = userTeamService.remove(userTeamQueryWrapper);
        if(!result){
            throw new BusinessException(ErrorCode.PARAMS_ERROR,"删除队伍关联信息失败");
        }
        //4. 删除队伍
        return this.removeById(team);
    }

    /**
     * 获取某队伍当前人数
     * @param teamId
     * @return
     */
    private long countTeamUserByTeamId(long teamId){
        QueryWrapper<UserTeam> userTeamQueryWrapper = new QueryWrapper<>();
        userTeamQueryWrapper.eq("teamId",teamId);
        return userTeamService.count(userTeamQueryWrapper);
    }

    /**
     * 根据Id获取队伍信息
     * @param teamId
     * @return
     */
    private Team getTeamById(Long teamId){
        if(teamId == null||teamId<=0){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        Team team = this.getById(teamId);
        if(team == null){
            throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在");
        }
        return team;
    }


    /**
     * 获取用户详细信息（关联队伍用户信息）
     *
     * @param teamQuery
     * @param request
     * @return
     */
    @Override
    public TeamUserVO getTeamInfo(TeamQuery teamQuery, HttpServletRequest request) {
        if(teamQuery == null){
            throw  new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        User loginUser = userService.getCurrentUser(request);


        Team team = this.getById(teamQuery.getId());
        User teamLeader = userService.getById(team.getUserId());

        if(team == null){
            throw new BusinessException(ErrorCode.NULL_ERROR,"队伍不存在");
        }
        TeamUserVO teamUserVO = new TeamUserVO();
        BeanUtils.copyProperties(team, teamUserVO);

        QueryWrapper<UserTeam> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("teamId",teamQuery.getId());
        List<UserTeam> userTeams = userTeamService.list(queryWrapper);
        List<Long> userIdList = userTeams.stream().map(UserTeam::getUserId).collect(Collectors.toList());
        // 当前用户不是管理员
        // 队伍中的用户ids中不包含传过来的当前用户的id
        // 当前用户的id不等于队伍的创建者id 说明没权限
        boolean noPermissions = !userService.isAdmin(loginUser) && !userIdList.contains(loginUser.getId()) && loginUser.getId() != team.getUserId();
        if (noPermissions) {
            throw new BusinessException(ErrorCode.NULL_AUTH, "暂无权限查看");
        }
        List<UserVO> userVOList = new ArrayList<>();

        teamUserVO.setHasJoinNum(userIdList.size());
        //获取创建人用户信息
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(teamLeader, userVO);
        teamUserVO.setCreateUser(userVO);
       for (Long userId : userIdList) {

            User user = userService.getById(userId);
            if(user!=null){
                userVO = new UserVO();
                BeanUtils.copyProperties(user, userVO);
                userVOList.add(userVO);
            }

        }
        teamUserVO.setUserVOList(userVOList);


        return teamUserVO;
    }

    /**
     * 获取我创建的队伍
     * @param teamQuery
     * @return
     */
    @Override
    public List<TeamUserVO> listMyCreate(TeamQuery teamQuery) {
        if(teamQuery == null){
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }
        QueryWrapper<Team> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("userId",teamQuery.getUserId());
        List<Team> teams = this.list(queryWrapper);
        List<TeamUserVO> teamUserVOList = new ArrayList<>();
        for (Team team : teams) {
            TeamUserVO teamUserVO = new TeamUserVO();
            BeanUtils.copyProperties(team, teamUserVO);
            teamUserVOList.add(teamUserVO);
        }

        return teamUserVOList;
    }
}




